﻿using System.Linq.Expressions;

namespace PmSoft.Core.Extensions;

/// <summary>
/// 表达式树的扩展方法
/// </summary>
public static class ExpressionExtensions
{
	/// <summary>
	/// 合并两个表达式树
	/// </summary>
	/// <typeparam name="T">表达式的类型</typeparam>
	/// <param name="first">第一个表达式</param>
	/// <param name="second">第二个表达式</param>
	/// <param name="merge">合并函数</param>
	/// <returns>合并后的表达式</returns>
	public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
	{
		var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
		var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
		return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
	}

	/// <summary>
	/// 表达式树的 AndAlso 逻辑
	/// </summary>
	/// <typeparam name="T">表达式的类型</typeparam>
	/// <param name="first">第一个表达式</param>
	/// <param name="second">第二个表达式</param>
	/// <returns>合并后的表达式</returns>
	public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
	{
		return first.Compose(second, Expression.AndAlso);
	}

	public static Expression<Func<T1, T2, bool>> AndAlso<T1, T2>(this Expression<Func<T1, T2, bool>> first, Expression<Func<T1, T2, bool>> second)
	{
		return first.Compose(second, Expression.AndAlso);
	}

	public static Expression<Func<T1, T2, T3, bool>> AndAlso<T1, T2, T3>(this Expression<Func<T1, T2, T3, bool>> first, Expression<Func<T1, T2, T3, bool>> second)
	{
		return first.Compose(second, Expression.AndAlso);
	}

	public static Expression<Func<T1, T2, T3, T4, bool>> AndAlso<T1, T2, T3, T4>(this Expression<Func<T1, T2, T3, T4, bool>> first, Expression<Func<T1, T2, T3, T4, bool>> second)
	{
		return first.Compose(second, Expression.AndAlso);
	}

	public static Expression<Func<T1, T2, T3, T4, T5, bool>> AndAlso<T1, T2, T3, T4, T5>(this Expression<Func<T1, T2, T3, T4, T5, bool>> first, Expression<Func<T1, T2, T3, T4, T5, bool>> second)
	{
		return first.Compose(second, Expression.AndAlso);
	}

	/// <summary>
	/// 表达式树的 OrElse 逻辑
	/// </summary>
	/// <typeparam name="T">表达式的类型</typeparam>
	/// <param name="first">第一个表达式</param>
	/// <param name="second">第二个表达式</param>
	/// <returns>合并后的表达式</returns>
	public static Expression<Func<T, bool>> OrElse<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
	{
		return first.Compose(second, Expression.OrElse);
	}

	public static Expression<Func<T1, T2, bool>> OrElse<T1, T2>(this Expression<Func<T1, T2, bool>> first, Expression<Func<T1, T2, bool>> second)
	{
		return first.Compose(second, Expression.OrElse);
	}

	public static Expression<Func<T1, T2, T3, bool>> OrElse<T1, T2, T3>(this Expression<Func<T1, T2, T3, bool>> first, Expression<Func<T1, T2, T3, bool>> second)
	{
		return first.Compose(second, Expression.OrElse);
	}

	public static Expression<Func<T1, T2, T3, T4, bool>> OrElse<T1, T2, T3, T4>(this Expression<Func<T1, T2, T3, T4, bool>> first, Expression<Func<T1, T2, T3, T4, bool>> second)
	{
		return first.Compose(second, Expression.OrElse);
	}

	public static Expression<Func<T1, T2, T3, T4, T5, bool>> OrElse<T1, T2, T3, T4, T5>(this Expression<Func<T1, T2, T3, T4, T5, bool>> first, Expression<Func<T1, T2, T3, T4, T5, bool>> second)
	{
		return first.Compose(second, Expression.OrElse);
	}

	/// <summary>
	/// 参数重绑定器，用于替换表达式中的参数
	/// </summary>
	private class ParameterRebinder : ExpressionVisitor
	{
		private readonly Dictionary<ParameterExpression, ParameterExpression> _map;

		private ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
		{
			_map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
		}

		/// <summary>
		/// 替换表达式中的参数
		/// </summary>
		/// <param name="map">参数映射</param>
		/// <param name="exp">表达式</param>
		/// <returns>替换后的表达式</returns>
		public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
		{
			return new ParameterRebinder(map).Visit(exp);
		}

		protected override Expression VisitParameter(ParameterExpression node)
		{
			if (_map.TryGetValue(node, out var replacement))
			{
				node = replacement;
			}
			return base.VisitParameter(node);
		}
	}
}

